package ptij.ADSL.de.ATUC;

import ptolemy.kernel.*;
import ptolemy.kernel.util.*;
import ptolemy.data.*;
import ptolemy.data.type.*;
import ptolemy.actor.*;
import ptolemy.domains.de.kernel.*;
import ptolemy.data.expr.Parameter;

import ptij.ADSL.common.InitSymbols;

//////////////////////////////////////////////////////////////////////////
//// Initializer
/**

@author Ivan Jeukens
*/
public class Initializer extends DEActor {    

    public Initializer(CompositeEntity container, String name) throws 
            NameDuplicationException, IllegalActionException {
        super(container, name);

        idftMode = new TypedIOPort(this, "idftMode", false, true);
	idftMode.setTypeEquals(BaseType.BOOLEAN);

	dftMode = new TypedIOPort(this, "dftMode", false, true);
	dftMode.setTypeEquals(BaseType.BOOLEAN);

	startTimer = new TypedIOPort(this, "startTimer", false, true);
	startTimer.setTypeEquals(BaseType.DOUBLE);
	
	stopTimer = new TypedIOPort(this, "stopTimer", false, true);
	stopTimer.setTypeEquals(BaseType.GENERAL);

        reset = new TypedIOPort(this, "reset", true, false);
	reset.setTypeEquals(BaseType.GENERAL);

        timerEvent = new TypedIOPort(this, "timerEvent", true, false);
	timerEvent.setTypeEquals(BaseType.GENERAL);
	
	start = new TypedIOPort(this, "start", false, true);
	start.setTypeEquals(BaseType.BOOLEAN);
	
        inputMode = new Parameter(this, "inputMode", new IntToken(1));
        _inputMode = 1;
        outputMode = new Parameter(this, "outputMode", new IntToken(1));
        _outputMode = 1;
        
        _createInputInterface();
        _createOutputInterface();
    }

    ///////////////////////////////////////////////////////////////////
    ////                     ports and parameters                  ////
    
    public TypedIOPort reset;
    public TypedIOPort fromATUR[];
    public TypedIOPort timerEvent;        
    public TypedIOPort start;
    public TypedIOPort toATUR[];
    public TypedIOPort idftMode;
    public TypedIOPort dftMode;
    public TypedIOPort startTimer;
    public TypedIOPort stopTimer;
    
    public Parameter inputMode;
    public Parameter outputMode;
    
    ///////////////////////////////////////////////////////////////////
    ////                         public methods                    ////

    public void attributeChanged(Attribute attribute)
            throws IllegalActionException {
        if(attribute == inputMode) {
	    /*IntToken it = (IntToken) inputMode.getToken();
	    _inputMode = it.intValue();
            try {
                _createInputInterface();
            }
            catch(NameDuplicationException ex) {
                ex.printStackTrace(System.out);
            }*/
        }
        else
        if(attribute == outputMode) {
            /*IntToken it = (IntToken) outputMode.getToken();
            _outputMode = it.intValue();
            try {
                _createOutputInterface();
            }
            catch(NameDuplicationException ex) {
                ex.printStackTrace(System.out);
            } */           
        }       
	else {
            super.attributeChanged(attribute);
        }        
    }
    
    public void initialize() throws IllegalActionException {
        super.initialize();

        _initSymbols = new InitSymbols();
        _symbol = new double[62];
	_lastsymbol = new double[62];
        _state = InitSymbols.C_QUIET1;
	_rxCounter = 0;
	_txCounter = 0;
        _lastFire = 0;
    }
    
    public void fire() throws IllegalActionException {
        if(timerEvent.hasToken(0)) {
	    timerEvent.get(0);
	    _timerEvent = true;
	}
	else {
	    _timerEvent = false;
	}

        _getSymbol();

        if(reset.hasToken(0)) {
//	    System.out.println("ATUC: Reset detected ... " +
//	            "starting initialization");
	    reset.get(0);
	    _state = _initSymbols.C_QUIET1;
//	    System.out.println("/// ATU-C : C_QUIET1 \\\\\\");
	    _C_ACTCounter = 0;
	    _txCounter = 0;
	    _rxCounter = 0;
	    idftMode.send(0, new BooleanToken(false));
	    dftMode.send(0, new BooleanToken(false));
	}
	else {
            switch(_state) {
                case InitSymbols.C_QUIET1: {
		    
		    _ret = _detectSignal(InitSymbols.R_ACT_REQ_S1, 
		           InitSymbols.R_ACT_REQ_S2);

		    if(_ret == 2) {
                        _state = InitSymbols.MY_CSILENT1;
//			System.out.println("/// ATU-C : MY_CSILENT1 \\\\\\");
			startTimer.send(0, new DoubleToken(251));
//		        System.out.println("ATUC-Initializer: Detecting " + 
//	                    "R_ACT_REQ");
		    }
		    else
		    if(_ret == 1) {
//		        System.out.println("ATUC-Initializer: Detecting " + 
//	                    "R_ACT_REQ");
		    }
		    
		} break;

		case InitSymbols.MY_CSILENT1: {

		    if(!_hassymbol) {
		        _state = InitSymbols.C_ACT;
//			System.out.println("/// ATU-C : C_ACT \\\\\\");			
			_C_ACTCounter++;
//			System.out.println("ATUC-Initializer: # Detected " + 
//			    "R_ACT_REQ #");
		    }
		    else {
		        _state = InitSymbols.C_QUIET1;
//			System.out.println("/// ATU-C : C_QUIET1 \\\\\\");
	                _C_ACTCounter = 0;			
		    }
		    startTimer.send(0, new DoubleToken(250));
		    _rxCounter = 0;
		    
		} break;
		
		case InitSymbols.C_IDLE: {
		
		} break;
		
		case InitSymbols.C_TONE: {
		
		} break;

                case InitSymbols.C_ACT: {
		
		    _ret = _transmitSignal(InitSymbols.C_ACT2_S1,
		        InitSymbols.C_ACT2_S2);
//	            System.out.println("ATUC-Initializer: transmitted " +
//	                "C_ACT2");

		    if(_ret == 1) {
		        _txCounter = 0;
			_state = InitSymbols.C_QUIET2;
//                        System.out.println("/// ATU-C : C_QUIET2 \\\\\\");
			startTimer.send(0, new DoubleToken(250 * 129));
	            }
		    
		} break;
		
		case InitSymbols.C_QUIET2: {
                    		    
		    _ret = _detectSignal(InitSymbols.R_ACK2_S1,
		           InitSymbols.R_ACK2_S2);

		    if(_ret == 2) {
		        // _state = InitSymbols.C_REVEILLE;
			_state = InitSymbols.SHOWTIME;
			_rxCounter = 0;
			startTimer.send(0, new DoubleToken(250));
			if(!_timerEvent) stopTimer.send(0, new Token());
	                _C_ACTCounter = 0;

//                        System.out.println("/// ATU-C : C_REVEILLE \\\\\\");
//		        System.out.println("ATUC-Initializer: Detected " + 
//	                    "R_ACK2");
//			System.out.println(" %%%%%% ATUC : End of Activation " +
//			    " %%%%%%");
		    }
		    else
		    if(_ret == 0) {
		        if(_C_ACTCounter < 2) {
			    _state = InitSymbols.C_ACT;
//                            System.out.println("/// ATU-C : C_ACT \\\\\\");
                            _C_ACTCounter++;
			}
			else {
	                    _state = InitSymbols.C_QUIET1;
//                            System.out.println("/// ATU-C : C_QUIET1 \\\\\\");
	                    _C_ACTCounter = 0;
			}
	                _rxCounter = 0;
                        if(!_timerEvent) stopTimer.send(0, new Token());
		    }
		    else
		    if(_ret == 1) {
		        if(_timerEvent) {
//			    System.out.println("ATUC # C_QUIET2 -> Time out");
			    if(_C_ACTCounter < 2) {
			        _state = InitSymbols.C_ACT;
//                                System.out.println("/// ATU-C : C_ACT \\\\\\");
				_C_ACTCounter++;
			    }
			    else {
			        _state = InitSymbols.C_QUIET1;
//                                System.out.println("/// ATU-C : C_QUIET1 \\\\\\");
				_C_ACTCounter = 0;
			    }
			    _rxCounter = 0;
			}
			else {
//		            System.out.println("ATUC-Initializer: Detecting " + 
//	                        "R_ACK2");
			}
		    }
		
		} break;

		case InitSymbols.C_REVEILLE: {
		
                    _ret = _transmitSignal(InitSymbols.C_REVEILLE_S1,
		        InitSymbols.C_REVEILLE_S2);
//	            System.out.println("ATUC-Initializer: transmitted " +
//	                "C_REVEILLE");
		
		    if(_ret == 1) {
		        _txCounter = 0;
			_state = InitSymbols.C_PILOT1;
//                        System.out.println("/// ATU-C : C_PILOT1 \\\\\\");			
			((DEDirector)getDirector()).fireAt(this, _currentFire + 1);
			startTimer.send(0, new DoubleToken(4436 * 250));
		    }

		} break;
		
		case InitSymbols.C_PILOT1: {

                    _sendSymbol(InitSymbols.C_PILOT_S);
//		    System.out.println("ATUC-Initializer: transmitted " +
//	                "C_PILOT");

                    if(_hassymbol && ((_currentFire - _lastFire) == 250)) {
                        if(_initSymbols.checkATURSymbol(InitSymbols.R_REVERB_S,
			    _symbol)) {
		            _rxCounter++;
//		            System.out.println("ATUC-Initializer: Detecting " + 
//	                        "R_REVERB1");
			    if(_rxCounter == 512/_BASE) {
		                _state = InitSymbols.C_REVERB1;
//                                System.out.println("/// ATU-C : C_REVERB1 \\\\\\");
			        _rxCounter = 0;
			        if(!_timerEvent) stopTimer.send(0, new Token());				
			        startTimer.send(0, new DoubleToken(1.0));
				// fireAt(_currentFire);
//		                System.out.println("ATUC-Initializer: Detected "
//				    + "R_REVERB1");
			    }
		        }
		        else {
		            _rxCounter = 0;
                        }
	            }
	            else {
	                _rxCounter = 0;
	            }

		    if(_timerEvent) {
//			System.out.println("ATUC # C_PILOT1 -> Time out");
			_state = InitSymbols.C_QUIET1;
//                        System.out.println("/// ATU-C : C_QUIET1 \\\\\\");
		    }
		
		} break;
		
		case InitSymbols.C_REVERB1: {

                    if(_timerEvent) {
                        if(_txCounter < 512/_BASE) {
		            _sendSymbol(InitSymbols.C_REVERB_S);
			    startTimer.send(0, new DoubleToken(249));
			    _txCounter++;
//	                    System.out.println("ATUC-Initializer: transmitted "
//			        + "C_REVERB");
		        }
		        else {
		            _txCounter = 0;
			    _state = InitSymbols.C_PILOT2;
//                            System.out.println("/// ATU-C : C_PILOT2 \\\\\\");
			    startTimer.send(0, new DoubleToken(1.0));
			    // fireAt(_currentFire);
		        }
		    }
		
		} break;
		
		case InitSymbols.C_PILOT2: {

                    if(_timerEvent) {
                        if(_txCounter < 3072/_BASE) {
                            _sendSymbol(InitSymbols.C_PILOT_S);
		            _txCounter++;
			    startTimer.send(0, new DoubleToken(249));
//	                    System.out.println("ATUC-Initializer: transmitted "
//			        + "C_PILOT");
		        }
		        else {
		            _txCounter = 0;
			    _state = InitSymbols.C_ECT;
//                            System.out.println("/// ATU-C : C_ECT \\\\\\");
			    startTimer.send(0, new DoubleToken(250 * 512/_BASE));
		        }
		    }

		} break;

		case InitSymbols.C_ECT: {
		
		    if(_timerEvent) {
		        _state = InitSymbols.C_REVERB2;
//                        System.out.println("/// ATU-C : C_REVERB2 \\\\\\");
			// fireAt(_currentFire);
			startTimer.send(0, new DoubleToken(1.0));
		    }
		
		} break;
		
		case InitSymbols.C_REVERB2: {

                    if(_timerEvent) {
		        if(_txCounter < 1536/_BASE) {
		            _sendSymbol(InitSymbols.C_REVERB_S);
			    startTimer.send(0, new DoubleToken(249));
			    _txCounter++;
//	                    System.out.println("ATUC-Initializer: transmitted "
//			        + "C_REVERB");
		        }
		        else {
		            _txCounter = 0;
			    _state = InitSymbols.C_PILOT3;
//                            System.out.println("/// ATU-C : C_PILOT3 \\\\\\");
			    startTimer.send(0, new DoubleToken(1.0));
			    // fireAt(_currentFire);
		        }
		    }

		} break;
		
		case InitSymbols.C_PILOT3: {

                    if(_timerEvent) {
                        if(_txCounter < 512/_BASE) {
                            _sendSymbol(InitSymbols.C_PILOT_S);		    
		            _txCounter++;
			    startTimer.send(0, new DoubleToken(249));			
//	                    System.out.println("ATUC-Initializer: transmitted "
//			        + "C_PILOT");
		        }
		        else {
		            _txCounter = 0;
			    _state = InitSymbols.C_REVERB3;
//                            System.out.println("/// ATU-C : C_REVERB3 \\\\\\");
			    startTimer.send(0, new DoubleToken(1.0));
			    // fireAt(_currentFire);
		        }
		    }
		    		
		} break;
		
		case InitSymbols.C_REVERB3: {

                    if(_timerEvent) {
                        if(_txCounter < 1024/_BASE) {
		            _sendSymbol(InitSymbols.C_REVERB_S);
			    startTimer.send(0, new DoubleToken(249));
			    _txCounter++;
//	                    System.out.println("ATUC-Initializer: transmitted "
//			        + "C_REVERB");
		        }
		        else {
		            _txCounter = 0;
			    _state = InitSymbols.SHOWTIME;
//                            System.out.println("/// ATU-C : SHOWTIME \\\\\\");			    
//			    System.out.println(" %%%%%%% ATU-C : End of " +
//			        " Transceiver training %%%%%%%");
			    ((DEDirector)getDirector()).fireAt(this, _currentFire + 1);
			    // fireAt(_currentFire);
		        }
		    }
		
		} break;
		
		case InitSymbols.SHOWTIME: {
		    start.send(0, new BooleanToken(true));
		    idftMode.send(0, new BooleanToken(true));
		    dftMode.send(0, new BooleanToken(true));		    
//		    System.out.println("ATUC-Initialization done");
//  		    System.out.println("ATUC-Initializer: Showtime!");		    
		} break;
	    }
	}
    }    
    
    ///////////////////////////////////////////////////////////////////
    ////                         private methods                   ////
    
    private byte _detectSignal(int S1, int S2) throws
            IllegalActionException {

        if(_rxCounter == 0) {
	    if(_hassymbol) {
                if(_initSymbols.checkATURSymbol(S1, _symbol)) {
		    _rxCounter++;
		}
		else {
		    _rxCounter = 0;
		    return 0;
                }
	    }
	    else {
	        _rxCounter = 0;
		return 0;
	    }
	}
	else
        if(_rxCounter < 64/_BASE) {
	    if(_hassymbol && ((_currentFire - _lastFire) == 250)) {
                if(_initSymbols.checkATURSymbol(S1, _symbol)) {
		    _rxCounter++;
		}
		else {
		    _rxCounter = 0;
		    return 0;
                }
	    }
	    else {
	        _rxCounter = 0;
		return 0;
	    }
	}
	else
	if(_rxCounter < 128/_BASE) {
	    if(_hassymbol && ((_currentFire - _lastFire) == 250)) {
	        if(_initSymbols.checkATURSymbol(S2, _symbol)) {
		    _rxCounter++;
		    if(_rxCounter == 128/_BASE) {
		        _rxCounter = 0;
		        return 2;
		    }
	        }
		else {
		    _rxCounter = 0;
		    return 0;
		}
	    }
	    else {
	        _rxCounter = 0;
		return 0;
	    }
	}
	
	return 1;
    }

    private byte _transmitSignal(int S1, int S2) throws
            IllegalActionException {

        if(_txCounter < 64/_BASE) {
	    _sendSymbol(S1);
	    startTimer.send(0, new DoubleToken(249));
	    _txCounter++;
	}
        else
	if(_txCounter < 128/_BASE) {
	    _sendSymbol(S2);
	    _txCounter++;
	    if(_txCounter == 128/_BASE) return 1;
            startTimer.send(0, new DoubleToken(249));
	}

	return 0;
    }
    
    private void _getSymbol() throws IllegalActionException {

        _lastFire = _currentFire;
	_currentFire = ((DEDirector)getDirector()).getCurrentTime();
	
        if(fromATUR[0].hasToken(0)) {
	    for(int i = 0;i < _symbol.length;i++) {
                _lastsymbol[i] = _symbol[i];
	    }
	    if(_inputMode == 0) {
	        DoubleToken dt;
	        for(int i = 0;i < 31;i++) {
	            dt = (DoubleToken) fromATUR[i].get(0);
		    _symbol[2*i] = dt.doubleValue();

	            dt = (DoubleToken) fromATUR[i].get(0);
		    _symbol[2*i + 1] = dt.doubleValue();
	        }
	    }
	    else {
	        ObjectToken ot = (ObjectToken) fromATUR[0].get(0);
		_symbol = (double []) ot.getValue();
	    }
	    _hassymbol = true;
	}
	else {
	    for(int i = 0;i < _symbol.length;i++) {
                _lastsymbol[i] = _symbol[i];
	        _symbol[i] = 0;
	    }
	    _hassymbol = false;
	}
    }

    private void _sendSymbol(int symbol) throws IllegalActionException {    
        double [] s = _initSymbols.getATUCSymbol(symbol);
	if(_outputMode == 0) {
            for(int i = 0;i < 255;i++) {
	        toATUR[i].send(0, new DoubleToken(s[2*i]));
	        toATUR[i].send(0, new DoubleToken(s[2*i + 1]));
  	    }
	}
	else {
	    toATUR[0].send(0, new ObjectToken(s));
	}
    }

    private void _createInputInterface() throws IllegalActionException, 
            NameDuplicationException {	
	if(_inputMode == 0) {
	    fromATUR = new TypedIOPort[31];
	    for(int i = 0;i < 31;i++) {
	        fromATUR[i] = new TypedIOPort(this, "fromATUR_" + i, 
		        true, false);
	        fromATUR[i].setTypeEquals(BaseType.DOUBLE);
	    }
	}
	else
	if(_inputMode == 1) {
	    fromATUR = new TypedIOPort[1];
	    fromATUR[0] = new TypedIOPort(this, "fromATUR", true, false);
	    fromATUR[0].setTypeEquals(BaseType.OBJECT);
	}
	else {
	    throw new IllegalActionException(this, "Invalid fromATURMode");	
	}
    }

    private void _createOutputInterface() throws IllegalActionException, 
            NameDuplicationException {
        if(_outputMode == 0) {	
	    toATUR = new TypedIOPort[255];
	    for(int i = 0;i < 255;i++) {
	        toATUR[i] = new TypedIOPort(this, "toATUR_" + i, false, true);
	        toATUR[i].setTypeEquals(BaseType.DOUBLE);
	    }
	}
	else
	if(_outputMode == 1) {
	    toATUR = new TypedIOPort[1];
	    toATUR[0] = new TypedIOPort(this, "toATUR", false, true);
	    toATUR[0].setTypeEquals(BaseType.OBJECT);
	}
	else {
	    throw new IllegalActionException(this, "Invalid toATURMode");
	}	
    }

    ///////////////////////////////////////////////////////////////////
    ////                         private variables                 ////

    private static final int _BASE = 32;

    private int _C_ACTCounter;

    private int _state;
    private byte _ret;
    private InitSymbols _initSymbols;
    private boolean _timerEvent;

    private int _rxCounter;
    private int _txCounter;

    private double [] _symbol;
    private double _currentFire;
    private double [] _lastsymbol;
    private double _lastFire;
    private boolean _hassymbol;
    
    private int _inputMode;
    private int _outputMode;
}
